
#include <stdint.h>
#include <cmsis_armcc.h> // for __set_FAULTMASK()
#include <stm32f10x_iwdg.h>
#include <stm32f10x_flash.h>

#include "share/cccompat.h"
#include "share/utils.h"
#include "mcu.h"

#include "share/log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "MCU"
#include "share/log4app.h"

// STM32 是小端模式
bool mcu_is_big_endian() { return false; }

size_t mcu_align_bytes() {
	// STM32 F0xx/L0xx 系列 MCU 是 4 字节对齐
	// 如果地址未对齐，不可访问 16/32 位变量
	return 4;
}

uint64_t mcu_get_beu64(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return ((uint64_t)buf[0] << 56) | ((uint64_t)buf[1] << 48) | ((uint64_t)buf[2] << 40) | ((uint64_t)buf[3] << 32) |
	       ((uint64_t)buf[4] << 24) | ((uint64_t)buf[5] << 16) | ((uint64_t)buf[6] << 8 ) | ((uint64_t)buf[7]);
}

uint32_t mcu_get_beu32(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3]);
}

uint16_t mcu_get_beu16(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return (buf[0] << 8) | (buf[1]);
}

uint64_t mcu_get_leu64(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return ((uint64_t)buf[7] << 56) | ((uint64_t)buf[6] << 48) | ((uint64_t)buf[5] << 40) | ((uint64_t)buf[4] << 32) |
	       ((uint64_t)buf[3] << 24) | ((uint64_t)buf[2] << 16) | ((uint64_t)buf[1] << 8 ) | ((uint64_t)buf[0]);
}

uint32_t mcu_get_leu32(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);
}

uint16_t mcu_get_leu16(const void *stream) {
	uint8_t *buf = (uint8_t*)stream;
	return (buf[1] << 8) | (buf[0]);
}

void* mcu_get_pointer(const void *stream) {
	// Xilinx-A9 是小端模式
	switch(mcu_pointer_size()) {
	case 8:  return (void*)(uintptr_t)mcu_get_leu64(stream);
	case 4:  return (void*)(uintptr_t)mcu_get_leu32(stream);
	default: return (void*)(uintptr_t)mcu_get_leu16(stream);
	}
}

bool mcu_is_in_ram(uintptr_t addr, uintptr_t len) {
	return ((addr >= MCU_RAM_BEGIN_ADDRESS) && ((addr + len) < MCU_RAM_END_ADDRESS)) ||
	       ((addr >= MCU_DATA_RAM_BEGIN_ADDRESS) && ((addr + len) < MCU_DATA_RAM_END_ADDRESS));
}

bool mcu_is_in_flash(uintptr_t addr, uintptr_t len) {
	return (addr >= MCU_FLASH_BEGIN_ADDRESS) && ((addr + len) < MCU_FLASH_END_ADDRESS);
}

bool mcu_is_debugging() {
	return (CoreDebug->DHCSR & 1);
}

// 看门狗初始化
/*
 Prescaler divider      PR[2:0] bits                 Timeout (ms)
											 RL[11:0]= 0x000   RL[11:0]= 0xFFF
		/4                   0                    0.1            409.6
		/8                   1                    0.2            819.2
		/16                  2                    0.4            1638.4
		/32                  3                    0.8            3276.8
		/64                  4                    1.6            6553.6
		/128                 5                    3.2            13107.2
		/256                 6/7                  6.4            26214.4
 */
uint32_t mcu_watchdog_timeout_max() {
	return 26214;
}

std_err_t mcu_watchdog_init(uint32_t timeout_ms) {
    if(timeout_ms > mcu_watchdog_timeout_max()) return STD_ERR_OUT_OF_RANGE;
	IWDG_Enable();
	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
	IWDG_SetPrescaler(IWDG_Prescaler_256);
	IWDG_SetReload(timeout_ms*10 / 64);
	IWDG_ReloadCounter();
    return STD_ERR_OK;
}

void mcu_watchdog_feed(void) {
	IWDG_ReloadCounter();
}

void mcu_reboot(uint32_t reason) {
	// 在 RTC 后备区中存放重启原因
	mcu_rtc_bkp_t *bkp = mcu_rtc_bkp_read();
	bkp->boot_reason = reason;
	mcu_rtc_bkp_write(bkp);
	
	__set_PRIMASK(1);
	__set_FAULTMASK(1);
	NVIC_SystemReset();
}

uint32_t mcu_get_reboot_reason(void) {
	// 从 RTC 后备区中获取重启原因
	mcu_rtc_bkp_t *bkp = mcu_rtc_bkp_read();
	return bkp ? bkp->boot_reason : 0;
}

void mcu_clear_reboot_reason(void) {
	// 在 RTC 后备区中存放重启原因
	mcu_rtc_bkp_t *bkp = mcu_rtc_bkp_read();
	bkp->boot_reason = 0;
	mcu_rtc_bkp_write(bkp);
}

uint8_t* mcu_get_chip_id(void) {
	return (uint8_t*)0x1FFFF7E8;
}

uint32_t mcu_flash_page_addr(uint32_t addr) {
	return addr - (addr % MCU_FLASH_PAGE_SIZE);
}

// 擦除页
static std_err_t flash_erase(uint32_t addr, uint32_t len) {
	// 检查是否溢出 Flash
	if(!mcu_is_in_flash(addr, len)) { return STD_ERR_OUT_OF_RANGE; }
	// 检查是否是整页
	if((addr % MCU_FLASH_PAGE_SIZE) || (len % MCU_FLASH_PAGE_SIZE)) { return STD_ERR_INVALID_SIZE; }
	
	int npage = len / MCU_FLASH_PAGE_SIZE;
	for(int i=0; i<npage; i++) {
		// 擦除页
		FLASH_Status fs = FLASH_ErasePage(addr);
		if(FLASH_COMPLETE != fs) { return STD_ERR_FAIL; }
		// 下一页
		addr += MCU_FLASH_PAGE_SIZE;
	}
	
	return STD_ERR_OK;
}

// 写入整页数据
static std_err_t flash_write(uint32_t _addr, uint32_t size, const void *_data) {
    // 页起始地址(必须页地址对齐)
    uint32_t* addr = (uint32_t*)_addr;
    // WORD 数据指针
    const uint32_t* data = (uint32_t*)_data;
    // 总 WORD 数量
    uint16_t twc = size/4;
	// Flash 操作状态码/错误码
    FLASH_Status fs;

    // WORD 写
    for(int wc=0; wc < twc; wc++) {
        fs = FLASH_ProgramWord((uint32_t)addr, *data);
        if(FLASH_COMPLETE != fs) { return STD_ERR_FAIL; }
        addr++; data++; // 地址和数据同时向后移4个字节
    }
	
	return STD_ERR_OK;
}

std_err_t mcu_flash_write(uint32_t headAddr, uint32_t len, const void *payload) {
	if(!len || !payload || !mcu_is_in_flash(headAddr, len)) { return STD_ERR_INVALID_ARG; }
	// 分配页缓存
	uint8_t cache[MCU_FLASH_PAGE_SIZE];

	// !!! 注意：写 Flash 过程中，禁止中断 !!!
	mcu_pause_irqs(cs);
	// Flash 解锁
    FLASH_Unlock(); FLASH_WaitForLastOperation(500);
    //if(FLASH_COMPLETE != FLASH_WaitForLastOperation(500)) { osCriticalLeave(cs); return STD_ERR_FAIL; }
    FLASH_ClearFlag(FLASH_FLAG_BSY|FLASH_FLAG_EOP|FLASH_FLAG_PGERR|FLASH_FLAG_WRPRTERR); // 清除标志位
	//{{
	
	// 尾部地址
	uint32_t tailAddr = headAddr + len;
	// 缓存数据游标
	uint8_t *pc = cache;
	// 净荷数据游标
	const uint8_t *pp = payload;
	
	// 数据头所在的目标页地址
	uint32_t headPageAddr = mcu_flash_page_addr(headAddr);
	// 数据头所在的目标页中有多少 dummy 数据
	uint32_t headDummy = headAddr - headPageAddr;
	// 数据头所在的目标页中能存放多少数据
	uint32_t headPayload = MCU_FLASH_PAGE_SIZE - headDummy;
	
	// 数据尾所在的目标页地址
	uint32_t tailPageAddr = mcu_flash_page_addr(tailAddr);
	// 数据尾所在的目标页中能存放多少数据
	uint32_t tailPayload = tailAddr - tailPageAddr;
	// 数据尾所在的目标页中有多少 dummy 数据
	uint32_t tailDummy = MCU_FLASH_PAGE_SIZE - tailPayload;
	
	if(headPageAddr == tailPageAddr) {
		// 头尾在同一页中，也就是说一页就可以写完
		// 读入头部的 dummy 数据到页缓存
		memcpy(pc, (void*)headPageAddr, headDummy); pc += headDummy;
		// 净荷写入页缓存
		memcpy(pc, pp, len); pc += len; pp += len;
		// 读入尾部的 dummy 数据到页缓存
		memcpy(pc, (void*)tailAddr, tailDummy); pc += tailDummy;
		// 擦除
		flash_erase(headPageAddr, MCU_FLASH_PAGE_SIZE);
		// 写入缓存
		flash_write(headPageAddr, MCU_FLASH_PAGE_SIZE, cache); pc = cache;
	} else {
		// 写入头部页
		// 读入头部的 dummy 数据到页缓存
		memcpy(pc, (void*)headPageAddr, headDummy); pc += headDummy;
		// 净荷头部写入页缓存
		memcpy(pc, pp, headPayload); pc += headPayload; pp += headPayload;
		// 擦除
		flash_erase(headPageAddr, MCU_FLASH_PAGE_SIZE);
		// 写入缓存
		flash_write(headPageAddr, MCU_FLASH_PAGE_SIZE, cache); pc = cache;
		
		// 中间页首地址
		uint32_t bodyPageAddr = headPageAddr + MCU_FLASH_PAGE_SIZE;
		// 中间页数据总长度
		uint32_t bodySize = tailPageAddr - bodyPageAddr;
		// 判断是否有中间页
		if(bodySize) {
			// 判断净荷是否也是在 SFFS 存储区中
			if(mcu_flash_page_addr((uint32_t)pp)) {
				// 净荷在 SFFS 中，必须一次写一页，否则可能有问题
				for(; bodyPageAddr < tailPageAddr; bodyPageAddr += MCU_FLASH_PAGE_SIZE) {
					// 净荷写入页缓存
					memcpy(cache, pp, MCU_FLASH_PAGE_SIZE); pp += MCU_FLASH_PAGE_SIZE;
					// 擦除
					flash_erase(bodyPageAddr, MCU_FLASH_PAGE_SIZE);
					// 写入缓存
					flash_write(bodyPageAddr, MCU_FLASH_PAGE_SIZE, cache);
				}
			} else {
				// 净荷不是在 SFFS 中，可以一次性写入中间页
				// 整体擦除
				flash_erase(bodyPageAddr, bodySize);
				// 整体写入
				flash_write(bodyPageAddr, bodySize, pp); pp += bodySize;
			}
		}
		
		// 写入尾部页
		// 净荷尾部写入页缓存
		memcpy(pc, pp, tailPayload); pc += tailPayload; pp += tailPayload;
		// 读入尾部的 dummy 数据到页缓存
		memcpy(pc, (void*)tailAddr, tailDummy); pc += tailDummy;
		// 擦除
		flash_erase(tailPageAddr, MCU_FLASH_PAGE_SIZE);
		// 写入缓存
		flash_write(tailPageAddr, MCU_FLASH_PAGE_SIZE, cache); pc = cache;
	}

	//}} ROM 上锁
	FLASH_Lock(); FLASH_WaitForLastOperation(500);
	// 恢复中断状态
	mcu_resume_irqs(cs);
	
	return STD_ERR_OK;
}

std_err_t mcu_init(func_bsp_enable_bus_clock bsp_enable_bus_clock, func_bsp_set_irq_priorities bsp_set_irq_priorities) {
	static std_err_t err;
	
	// 调用 BSP 提供的函数: 使能外设时钟
	if(bsp_enable_bus_clock) {
		err = bsp_enable_bus_clock();
		retonerr(err, "bsp_enable_bus_clock(): %s\r\n", std_err_comment(err));
	}
	
	// 初始化时钟
	err = mcu_clock_init();
	retonerr(err, "_mcu_system_core_clock_set_hse_pll(): %s\r\n", std_err_comment(err));

	// 初始化 NVIC
	mcu_intc_init();
	
	// 调用 BSP 提供的函数: 初始化中断优化先级
	if(bsp_set_irq_priorities) {
		err = bsp_set_irq_priorities();
		retonerr(err, "bsp_enable_bus_clock(): %s\r\n", std_err_comment(err));
	}
	
	return STD_ERR_OK;
}
